/**
 *
 * 同步队列：AQS会把所有的请求线程构成一个CLH队列，当一个线程执行完毕（lock.unlock()）时会激活自己的后继节点，但正在执行的线程并不在队列中，而那些等待执行的线程全部处于阻塞状态(park())
 * 抽象类：AQS内部维护着一个FIFO队列，该队列就是CLH同步队列。
 * @see java.util.concurrent.locks.AbstractQueuedSynchronizer
 * @see java.util.concurrent.locks.AbstractQueuedSynchronizer#head 头结点，你直接把它当做 当前持有锁的线程 ???
 * @see java.util.concurrent.locks.AbstractQueuedSynchronizer#tail
 * @see java.util.concurrent.locks.AbstractQueuedSynchronizer#state
 * @see java.util.concurrent.locks.AbstractQueuedSynchronizer#enq(java.util.concurrent.locks.AbstractQueuedSynchronizer.Node) Inserts node into queue
 *
 * @see java.util.concurrent.locks.AbstractQueuedSynchronizer.ConditionObject ?? 实现了Condition接口
 *
 * @see java.util.concurrent.locks.AbstractQueuedSynchronizer.Node
 * @see java.util.concurrent.locks.AbstractQueuedSynchronizer.Node#SHARED 共享
 * @see java.util.concurrent.locks.AbstractQueuedSynchronizer.Node#EXCLUSIVE 独占
 * @see java.util.concurrent.locks.AbstractQueuedSynchronizer.Node#waitStatus
 * @see java.util.concurrent.locks.AbstractQueuedSynchronizer.Node#prev 前驱节点
 * @see java.util.concurrent.locks.AbstractQueuedSynchronizer.Node#next 后继节点
 * @see java.util.concurrent.locks.AbstractQueuedSynchronizer.Node#thread 当前节点排队的线程
 * @see java.util.concurrent.locks.AbstractQueuedSynchronizer.Node#nextWaiter

 * CLH队列全称是(Craig, Landin, andHagersten) lock queue
 *
 * CLH同步队列是一个FIFO双向队列，AQS依赖它来完成同步状态的管理，当前线程如果获取同步状态失败时，
 * AQS则会将当前线程已经等待状态等信息构造成一个节点（Node）并将其加入到CLH同步队列，同时会阻塞当前线程，
 * 当同步状态释放时，会把首节点唤醒（公平锁），使其再次尝试获取同步状态。
 * 在CLH同步队列中，一个节点表示一个线程，它保存着线程的引用（thread）、状态（waitStatus）、前驱节点（prev）、后继节点（next）
 *
 * AQS定义两种资源共享方式：Exclusive(独占,只有一个线程能执行)和Share(共享，多个线程可同时执行，如Semaphore/CountDownLatch).
 * 以ReentrantLock为例，state初始化为0，表示未锁定状态，A线程lock时，会调用tryAcquire()独占该锁并将该锁state + 1,
 *      此后，其他线程再try Acquire()时就会失败，直到A线程unlock到state=0(即释放锁),其他线程才有机会获取该锁，当然释放锁之前，A线程自己是可以重复获取此锁的，state就是会累加，这就是可重入的概念，但要注意，获取多少次就要释放多少次，这样才能保证state能回到零态
 * 以CountDownLatch为例，任务分为N个子线程去执行，state也初始化为N，与线程个数一致，这N个子线程是并行执行的，每个子线程执行完后countDown一次，state会减一，等到所有子线程都执行完后(即state=0),会unpark()唤醒主线程，然后主调用会从await返回，继续后续操作。
 *
 * 一般来说，自定义同步器要么是独占方式，要么是共享方式，他们也只需要实现tryAccquire-tryRelease,tryAcquireShared-tryReleaseShared中一种即可，但AQS也支持自定义同时实现独占和共享两种方式，如ReentrantReadWriteLock
 *
 * 相关子类
 * @see java.util.concurrent.locks.ReentrantLock.Sync
 * @see java.util.concurrent.locks.ReentrantReadWriteLock.Sync
 * @see java.util.concurrent.Semaphore.Sync
 * @see java.util.concurrent.CountDownLatch.Sync
 * @see java.util.concurrent.CyclicBarrier#lock 引用ReentrantLock
 *
 * jdk1.8不在依赖AbstractQueuedSynchronizer，直接使用unsafe内部函数
 * @see java.util.concurrent.FutureTask
 *
 *
 * AQS 结构
 * 先来看看 AQS 有哪些属性，搞清楚这些基本就知道 AQS 是什么套路了，毕竟可以猜嘛！
 *
 * // 头结点，你直接把它当做 当前持有锁的线程 可能是最好理解的
 * private transient volatile Node head;
 * // 阻塞的尾节点，每个新的节点进来，都插入到最后，也就形成了一个隐视的链表
 * private transient volatile Node tail;
 * // 这个是最重要的，不过也是最简单的，代表当前锁的状态，0代表没有被占用，大于0代表有线程持有当前锁
 * // 之所以说大于0，而不是等于1，是因为锁可以重入嘛，每次重入都加上1
 * private volatile int state;
 * // 代表当前持有独占锁的线程，举个最重要的使用例子，因为锁可以重入
 * // reentrantLock.lock()可以嵌套调用多次，所以每次用这个来判断当前线程是否已经拥有了锁
 * // if (currentThread == getExclusiveOwnerThread()) {state++}
 * private transient Thread exclusiveOwnerThread; //继承自AbstractOwnableSynchronizer
 *
 * 总结:
 *
 * 调用独占 自定义同步器中的tryAcquire()尝试获取资源，如果成功则直接返回。
 *
 * 没成功，则addWaiter()将该线程放入等待队列的尾部，并且标记为独占模式
 *
 * acquireQueued()使线程在等待队列中休息，有机会时(轮到自己，会被unpark())会去尝试获取资源，获得资源后才返回，如果整个等待过程中被中断，则返回true，否则返回false
 *
 * 如果线程在等待过程中被中断过，它是不响应的，只是获取资源后才进行自我中断，将中断补上。
 *
 * AbstractQueuedSynchronizer的release(int)方法
 *
 * release()是独占模式下线程释放共享资源的顶层入口，它会释放指定量的资源，如果彻底释放了，它会唤醒等待队列里的其他线程来获取资源。
 *
 * acquireShared(int) 此方法是共享模式下线程获取共享资源的顶层入口，它会获取指定量的资源，获取成功则进入等待队列，直到获取到资源为止，整个过程忽略中断
 *
 * 总结：tryAcquireShared()尝试获取资源，成功则直接返回；失败则通过doAcquireShared()进入等待队列park(),直到被unpark()/interrupt()并成功获取到资源才返回，
 * acquire()和acquireshare()两种方法下，线程在等待队列中都是忽略中断的，AQS也支持响应中断的，acquireInterruptibly()/acquireSharedInterruptibly() 即是。
 *
 * 实例:
 * Lock, ReentrantLock, ReentrantReadWriteLock
 *
 * Lock 是一个接口，定义了锁的基本方法
 * ReentrantLock实现了lock
 * ReentrantReadWriteLock和 ReentrantLock不同，加读锁时其他线程可以进行读操但不能进行写操作，加写锁时其他线程读写操作都不可进行。
 *
 * @date 2021/1/11
 */
package cn.jdemo.juc.aqs;